﻿using Common.Infrastructure.Structs;
using Common.Infrastructure.Utils;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace Common.Infrastructure.Service
{
    public class ServiceCallService : IServiceCallService
    {
        private readonly IServiceProvider provider;
        private readonly IAddressService addressService;

        public ServiceCallService(IServiceProvider provider, IAddressService address)
        {
            this.addressService = address;
            this.provider = provider;
        }

        public async Task<T> PostExecuteAsync<T>(string serviceCode, string url, object obj = null)
        {
            return await DoExecuteAsync<T>(serviceCode, url, true, null, obj);
        }

        public async Task<T> GetExecuteAsync<T>(string serviceCode, string url, Dictionary<string, object> param = null)
        {
            return await DoExecuteAsync<T>(serviceCode, url, true, param, null);
        }

        public async Task<T> GetGetAsync<T>(string serviceCode, string url, Dictionary<string, object> param = null)
        {
            var ret = await GetExecuteAsync<ApiActionResult<T>>(serviceCode, url, param);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return default;
        }

        public async Task<T> PostGetAsync<T>(string serviceCode, string url, object obj = null)
        {
            var ret = await PostExecuteAsync<ApiActionResult<T>>(serviceCode, url, obj);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return default;
        }

        public async Task<bool> GetDoAsync(string serviceCode, string url, Dictionary<string, object> param = null)
        {
            var ret = await GetExecuteAsync<ApiActionResult<bool>>(serviceCode, url, param);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return false;
        }

        public async Task<bool> PostDoAsync(string serviceCode, string url, object obj = null)
        {
            var ret = await PostExecuteAsync<ApiActionResult<bool>>(serviceCode, url, obj);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return false;
        }

        public async Task<List<T>> GetListAsync<T>(string serviceCode, string url, Dictionary<string, object> param = null)
        {
            var ret = await GetExecuteAsync<ApiActionResult<List<T>>>(serviceCode, url, param);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return null;
        }

        public async Task<List<T>> PostListAsync<T>(string serviceCode, string url, object obj)
        {
            var ret = await PostExecuteAsync<ApiActionResult<List<T>>>(serviceCode, url, obj);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return null;
        }

        public async Task<ApiPagedResult<T>> GetApiPageAsync<T>(string serviceCode, string url, Dictionary<string, object> param = null)
        {
            var ret = await GetExecuteAsync<ApiActionResult<ApiPagedResult<T>>>(serviceCode, url, param);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return null;
        }

        public async Task<ApiPagedResult<T>> PostApiPageAsync<T>(string serviceCode, string url, object obj)
        {
            var ret = await PostExecuteAsync<ApiActionResult<ApiPagedResult<T>>>(serviceCode, url, obj);
            if (ret != null && ret.IsSuccess)
            {
                return ret.Data;
            }
            else
            {
                ThrowErrorResult(ret);
            }

            return null;
        }

        private async Task<T> DoExecuteAsync<T>(string serviceCode, string url, bool isPost, Dictionary<string, object> param, object obj)
        {
            var hostPost = this.addressService.GetServiceAddress(serviceCode);
            url = hostPost + "/api/" + url;

            Dictionary<string, string> headParams = new Dictionary<string, string>();
            IHttpClientFactory clientFactory = null;
            try
            {
                if (this.provider != null)
                {
                    clientFactory = this.provider.GetService<IHttpClientFactory>();
                }
            }
            catch
            {
            }

            try
            {
                var tokenService = this.provider?.GetService<IUserTokenService>();
                var token = tokenService?.GetBearerAuth();
                if (!string.IsNullOrEmpty(token))
                {
                    headParams.Add("Authorization", token);
                }
            }
            catch
            {
            }

            if (clientFactory != null)
            {
                var webClient = clientFactory.CreateClient();
                if (!isPost)
                {
                    WebApiGetRequest request = new WebApiGetRequest
                    {
                        Url = url,
                        Params = param
                    }; 
                    headParams.ForEach(t => request.HeadParams.Add(t.Key, t.Value));
                    var response = await WebApiUtils.GetByClientAsync(webClient, request);
                    return string.IsNullOrEmpty(response.Data) ? default(T) : response.Data.ToObjectFromJson<T>();
                }
                else
                {
                    WebApiPostRequest request = new WebApiPostRequest
                    {
                        Url = url,
                        Params = param,
                        Obj = obj
                    };
                    headParams.ForEach(t => request.HeadParams.Add(t.Key, t.Value));
                    var response = await WebApiUtils.PostByClientAsync(webClient, request); 
                    return string.IsNullOrEmpty(response.Data) ? default(T) : response.Data.ToObjectFromJson<T>();
                }
            }
            else
            {
                using (HttpClient webClient = new HttpClient())
                {
                    if (!isPost)
                    {
                        WebApiGetRequest request = new WebApiGetRequest
                        {
                            Url = url,
                            Params = param
                        };
                        headParams.ForEach(t => request.HeadParams.Add(t.Key, t.Value));
                        var response = await WebApiUtils.GetByClientAsync(webClient, request);
                        return string.IsNullOrEmpty(response.Data) ? default(T) : response.Data.ToObjectFromJson<T>();
                    }
                    else
                    {
                        WebApiPostRequest request = new WebApiPostRequest
                        {
                            Url = url,
                            Params = param,
                            Obj = obj
                        };
                        headParams.ForEach(t => request.HeadParams.Add(t.Key, t.Value));
                        var response = await WebApiUtils.PostByClientAsync(webClient, request); 
                        return string.IsNullOrEmpty(response.Data) ? default(T) : response.Data.ToObjectFromJson<T>();
                    }
                }
            }
        }

        private void ThrowErrorResult<T>(ApiActionResult<T> ret)
        {
            if (ret != null && !ret.IsSuccess)
            {
                var e = ret.Messages.FirstOrDefault(t => t.MessageType == EFMessageType.Error);
                ////ret.Messages.Where(t => t != e).ForEach(t => LibMessageUtils.AddMessage(null, t));
                if (e != null)
                {
                    throw new CodeException(e.Code, e.Message);
                }
            }
        }
    }
}
